﻿#include "urc2encrypt.h"

#include <openssl/evp.h>
#include <openssl/err.h>

#include <QString>
#include <QDebug>

struct ScopedPointerCipherCtxDeleter
{
    static inline void cleanup(EVP_CIPHER_CTX* pointer)
    {
        EVP_CIPHER_CTX_free(pointer);
    }
};

URC2Encrypt::URC2Encrypt()
{
}

URC2Encrypt::~URC2Encrypt()
{
}

QString URC2Encrypt::GetLastError()
{
    unsigned long lastError = ERR_peek_last_error();
    if (lastError == 0)
    {
        return "";
    }
    char errorString[256];
    ERR_error_string_n(lastError, errorString, sizeof(errorString));
    return errorString;
}

QByteArray URC2Encrypt::decrypt(QByteArray cyphertext, const unsigned char* key, const unsigned char* iv)
{
    // 初始化 OpenSSL 库
    QScopedPointer<EVP_CIPHER_CTX, ScopedPointerCipherCtxDeleter> ctx(EVP_CIPHER_CTX_new());

    // 设置解密算法、模式和填充方式
    if (EVP_DecryptInit_ex(ctx.data(), EVP_rc2_cbc(), nullptr, key, iv) != 1)
    {
        qCritical() << "EVP_DecryptInit_ex failed !!!";
        return QByteArray();
    }
    if (EVP_CIPHER_CTX_set_padding(ctx.data(), EVP_PADDING_PKCS7) != 1)    // 开启 PKCS7 填充
    {
        qCritical() << "EVP_CIPHER_CTX_set_padding failed !!!";
        return QByteArray();
    }

    // 计算解密后的长度
    QByteArray decryptData;
    int plaintextLen = cyphertext.length() + EVP_CIPHER_CTX_block_size(ctx.data());
    decryptData.resize(plaintextLen);
    // 解密
    int len;
    if (EVP_DecryptUpdate(ctx.data(), (unsigned char*)decryptData.data(), &len, reinterpret_cast<const unsigned char*>(cyphertext.data()), cyphertext.length()) != 1)
    {
        qCritical() << "EVP_DecryptUpdate failed !!!";
        return QByteArray();
    }
    int plaintextFinalLen;
    if (EVP_DecryptFinal_ex(ctx.data(), (unsigned char*)decryptData.data() + len, &plaintextFinalLen) != 1)
    {
        qCritical() << "EVP_DecryptFinal_ex failed !!!";
        return QByteArray();
    }
    len += plaintextFinalLen;

    return decryptData.left(len);
}

QByteArray URC2Encrypt::encrypt(QByteArray plaintext, const unsigned char* key, const unsigned char* iv)
{
    // 初始化 OpenSSL 库
    QScopedPointer<EVP_CIPHER_CTX, ScopedPointerCipherCtxDeleter> ctx(EVP_CIPHER_CTX_new());

    // 设置解密算法、模式和填充方式
    if (EVP_EncryptInit_ex(ctx.data(), EVP_rc2_cbc(), nullptr, key, iv) != 1)
    {
        qCritical() << "EVP_DecryptInit_ex failed !!!";
        return QByteArray();
    }
    if (EVP_CIPHER_CTX_set_padding(ctx.data(), EVP_PADDING_PKCS7) != 1)    // 开启 PKCS7 填充
    {
        qCritical() << "EVP_CIPHER_CTX_set_padding failed !!!";
        return QByteArray();
    }

    // 计算解密后的长度
    QByteArray encryptData;
    int plaintextLen = plaintext.length() + EVP_CIPHER_CTX_block_size(ctx.data());
    encryptData.resize(plaintextLen);
    // 解密
    int len;
    if (EVP_EncryptUpdate(ctx.data(), (unsigned char*)encryptData.data(), &len, reinterpret_cast<const unsigned char*>(plaintext.data()), plaintext.length()) != 1)
    {
        qCritical() << "EVP_DecryptUpdate failed !!!";
        return QByteArray();
    }
    int plaintextFinalLen;
    if (EVP_EncryptFinal_ex(ctx.data(), (unsigned char*)encryptData.data() + len, &plaintextFinalLen) != 1)
    {
        qCritical() << "EVP_DecryptFinal_ex failed !!!";
        return QByteArray();
    }
    len += plaintextFinalLen;

    return encryptData.left(len);
}
